var request = require('request');
var xml2js = require('xml2js');
var async = require('async');
var crypto = require('crypto');


/**
 * 微信支付初始化参数
 *
 * @author 王俊
 * @date 2018-04-17
 *
 * @param {Object} options 初始化参数
 * @param {Object} options.appId 微信开放平台应用标识
 * @param {Object} options.mchId 微信商户 id
 * @param {Object} options.key 微信商户 key
 * @param {Object} options.notifyUrl 微信商户 key
 * @constructor
 */
function WeChatPay(options) {

    // this.TRADE_TYPE = options.tradeType;
    this.APP_ID = options.appId;
    this.MCH_ID = options.mchId;
    this.KEY = options.key;
    this.NOTIFY_URL = options.notifyUrl;
}

/**
 * 获取随机字符串
 *
 * @author 王俊
 * @date 2018-04-17
 *
 * @param {Number} len 随机字符串长度
 * @param {Number} flg 0 或不传标记随机 大小写字母+数字, 1 标记 随机数字, 2 标记大小写字母
 * @return {string} 随机字符串
 */
WeChatPay.prototype.genNonceStr = function(len,flg){
    var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    if(flg === 1) {
        chars = '0123456789';
    } else if(flg === 2) {
        chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    }
    var maxPos = chars.length;
    var ret = "";
    var size = Number(len||'32');
    for(var i = 0; i < size; i++) {
        ret += chars.charAt(Math.floor(Math.random() * maxPos));
    }
    return ret;
};
/**
 * 转换为微信参数字符串
 *
 * @author 王俊
 * @date 2018-04-17
 *
 * @param {Object} args 微信参数
 * @return {string} 拼接后的参数, key=value&key2=value2 形式
 */
WeChatPay.prototype.toWxParamStr = function(args) {
    var keys = Object.keys(args);
    keys = keys.sort();
    var newArgs = {};
    keys.forEach(function (key) {
        if(args[key]!==null && (args[key]+'')!=='') {// 过滤空值项
            newArgs[key] = args[key];// 签名时候的参数不需要转换为小写的
        }
    });
    var string = '';
    for (var k in newArgs) {
        string += '&' + k + '=' + newArgs[k];
    }
    string = string.substr(1);
    return string;
};

/**
 * 计算微信支付签名
 *
 * @author 王俊
 * @date 2018-04-18
 *
 * @param {String} key 签名 key
 * @param {Object} param 待签名参数
 * @return {String} 签名字符串
 */
WeChatPay.prototype.calcWxPaySign = function(key, param) {
    var paramObj = param||{};
    var paramStr = this.toWxParamStr(paramObj);

    key = key || this.KEY;

    paramStr = paramStr + '&key='+key;
    console.log("CalcParam: "+paramStr);
    var sign = crypto.createHash('md5').update(paramStr,'utf8').digest('hex');
    sign = sign.toUpperCase();
    console.log("CalcSign: "+sign);
    return sign;
};

/**
 * 获取 xml 节点值
 *
 * @author 王俊
 * @date 2018-04-18
 *
 * @param {String} node_name xml 节点名称
 * @param {String} xml xml 字符串
 * @return {String} xml 节点值
 */
WeChatPay.prototype.getXMLNodeValue = function(node_name,xml){
    var ret = null;
    var tmp = xml.split("<"+node_name+">");
    var tmp2 = null;
    if(tmp && tmp.length>1){
        tmp2 = tmp[1].split("</"+node_name+">");
    }
    if(tmp2 && tmp.length>0){
        ret = tmp2[0];
    }
    return ret;
};

/**
 * 请求微信接口,创建统一订单
 *
 * @author 王俊
 * @date 2018-04-18
 *
 * @param {String} key 微信签名 key
 * @param {String} formData form 类型的字符串参数
 * @param {Function} cb 回调
 */
WeChatPay.prototype.reqWxApi_payUniOrd = function(key, formData, cb) {
    var THIS =this;
    var url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    request({url:url,method:'POST',body: formData},function(err,response,retBody){
        if(!err && response.statusCode === 200){
            var parser = new xml2js.Parser({ trim:true, explicitArray:false, explicitRoot:false });
            parser.parseString((retBody||'').toString("utf-8"), function(parseErr, parseRet){
                if(parseErr) {
                    console.log('WeChatPay.js [reqWxApi] parser xml Err: '+parseErr);
                    return cb(parseErr, null);
                }
                // 验证返回值签名
                var retSign = parseRet.sign||'';
                if(parseRet.sign){
                    delete parseRet.sign;
                }
                var calSign = THIS.calcWxPaySign(key, parseRet);

                console.log('retSign:[' + retSign + '] calSign:[' + calSign + ']');
                if (!retSign || !calSign || retSign.toUpperCase() !== calSign.toUpperCase()) {
                    return cb('[微信统一下单]返回数据非法', null);
                }
                // [return_code] 此字段是通信标识，非交易标识，交易是否成功需要查看result_code来判断
                if((parseRet.return_code||'')==='SUCCESS') {

                    // 以下字段在return_code 和result_code都为SUCCESS的时候有返回:
                    // [trade_type]: 交易类型(调用接口提交的交易类型，取值如下：JSAPI，NATIVE，APP)
                    // [prepay_id]: 预支付交易会话标识,(微信生成的预支付回话标识，用于后续接口调用中使用，该值有效期为2小时)
                    if((parseRet.result_code||'')==='SUCCESS') {
                        parseRet.sign = retSign;
                        return cb(null, parseRet);
                    } else {
                        return cb(parseRet.err_code||'[微信统一下单]交易失败', null);
                    }

                } else {
                    return cb(parseRet.return_msg||'[微信统一下单]通信失败', null);
                }
            });
        } else {
            return cb(err, null);
        }
    });
};

/**
 * 获取 APP 支付参数
 *
 * @author 王俊
 * @date 2018-04-18
 *
 * @param {Object} options APP 支付参数
 * @param {String} options.openId 微信用户 openId
 * @param {String} options.attach 订单附加数据
 * @param {String} options.body 订单内容
 * @param {String} options.ip 终端 ip
 * @param {String} options.outTradeNo 商户唯一订单号
 * @param {String} options.totalFee 订单金额, 单位: 分
 * @param {Function} callback 回调
 */
WeChatPay.prototype.appPay = function (options, callback) {

    var trade_type = "APP";
    var THIS = this;

    var paramForSign = {
        appid: this.APP_ID,
        attach: options.attach || '',
        body: options.body || '',
        mch_id: this.MCH_ID,
        nonce_str: this.genNonceStr(32,0),
        notify_url: this.NOTIFY_URL,
        openid: options.openId || '',
        out_trade_no: options.outTradeNo,
        spbill_create_ip: options.ip || "127.0.0.1",
        total_fee: options.totalFee,
        trade_type: trade_type
    };
    // 调用微信下单接口
    var formData  = "<xml>";
    formData  += "<appid>"+paramForSign.appid+"</appid>";  // 应用ID(必填)
    formData  += "<attach>"+paramForSign.attach+"</attach>"; // 附加数据
    formData  += "<body>"+paramForSign.body+"</body>";// 商品或支付单简要描述
    formData  += "<mch_id>"+paramForSign.mch_id+"</mch_id>";// 商户号(必填)
    formData  += "<nonce_str>"+paramForSign.nonce_str+"</nonce_str>";// 随机字符串,不长于32位(必填)
    formData  += "<notify_url>"+paramForSign.notify_url+"</notify_url>";// 通知地址(必填)
    formData  += "<openid>"+paramForSign.openid+"</openid>";
    formData  += "<out_trade_no>"+paramForSign.out_trade_no+"</out_trade_no>";// 商户订单号(必填)
    formData  += "<spbill_create_ip>"+paramForSign.spbill_create_ip+"</spbill_create_ip>";// 终端IP(必填)
    formData  += "<total_fee>"+paramForSign.total_fee+"</total_fee>";// 总金额(必填)
    formData  += "<trade_type>"+paramForSign.trade_type+"</trade_type>";// 交易类型(必填)
    formData  += "<sign>"+this.calcWxPaySign(this.KEY, paramForSign)+"</sign>";
    formData  += "</xml>";
    this.reqWxApi_payUniOrd(this.KEY, formData, function(retErr, retInfo){
        if(retErr || !retInfo || !retInfo.prepay_id) {
            return callback(retErr || '预支付交易会话标识获取失败', null);
        }
        var ns = THIS.genNonceStr(32,0);
        var ts = Math.floor(Date.now()/1000)+"";
        // 巨坑: 参数名需要小写 !!!???
        var paramForSign2 = {
            appid: THIS.APP_ID,
            partnerid: THIS.MCH_ID,
            prepayid: retInfo.prepay_id,
            noncestr: ns,
            timestamp: ts,
            package: "Sign=WXPay"
        };
        var sign2 = THIS.calcWxPaySign(this.key, paramForSign2);
        var payParams = {
            prepayId:retInfo.prepay_id,
            nonceStr:ns,
            timeStamp:ts,
            sign:sign2
        };
        return callback(null, payParams);
    });
};

/**
 * 获取 H5 支付参数
 *
 * @author 王俊
 * @date 2018-04-18
 *
 * @param {Object} options H5 支付参数
 */
WeChatPay.prototype.h5Pay = function (options) {

};
/**
 * 获取 WEB 支付参数
 *
 * @author 王俊
 * @date 2018-04-18
 *
 * @param {Object} options WEB 支付参数
 */
WeChatPay.prototype.webPay = function (options) {

};


module.exports = WeChatPay;